﻿// Fill out your copyright notice in the Description page of Project Settings.


#include "Play/StarrySkyPlayerController.h"

#include "AbilitySystemBlueprintLibrary.h"
#include "EnhancedInputSubsystems.h"
#include "NavigationPath.h"
#include "NavigationSystem.h"
#include "NiagaraFunctionLibrary.h"
#include "StarrySkyGameplayTags.h"
#include "AbilitySystem/StarrySkyAbilitySystemComponent.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "Components/SplineComponent.h"
#include "Input/StarrySkyInputComponent.h"
#include "Interaction/EnemyInterface.h"

AStarrySkyPlayerController::AStarrySkyPlayerController()
{
	bReplicates = true;
	Spline=CreateDefaultSubobject<USplineComponent>("Spline");
}

void AStarrySkyPlayerController::PlayerTick(float DeltaTime)
{
	Super::PlayerTick(DeltaTime);
	//实时检测光标
	CursorTrace();
	//通过路径移动到目标
	AutoRun();
	//瞄准目标
	LookUp(DeltaTime);
}

void AStarrySkyPlayerController::BeginPlay()
{
	Super::BeginPlay();
	check(AuraContext);
	//多人游戏中，不是本地玩家无法通过GetLocalPlayer获取到对应自己的输入组件所以需要做空指针判断
	if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
	{
		Subsystem->AddMappingContext(AuraContext, 0);
	}
	//设置鼠标光标
	bShowMouseCursor = true;
	DefaultMouseCursor = EMouseCursor::Default;
	//设置鼠标是否隐藏，是否锁定再屏幕中
	FInputModeGameAndUI InputModeData;
	InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
	InputModeData.SetHideCursorDuringCapture(false);
	SetInputMode(InputModeData);
}

void AStarrySkyPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();
	//获取自定义的组件
	UStarrySkyInputComponent* StarrySkyInputComponent = CastChecked<UStarrySkyInputComponent>(InputComponent);
	//绑定移动回调函数
	StarrySkyInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AStarrySkyPlayerController::Move);
	//绑定输入动作回调函数
	StarrySkyInputComponent->BindAbilityActions(InputConfig, this, &ThisClass::AbilityInputTagPressed, 
		&ThisClass::AbilityInputTagReleased, &ThisClass::AbilityInputTagHeld);
}

void AStarrySkyPlayerController::Move(const FInputActionValue& InputActionValue)
{
	//移动输入是一个2D向量，转换输入为2dVector格式。
	const FVector2D InputAxisVector = InputActionValue.Get<FVector2D>();
	//然后获取控制器的选择来找到向前的方法，这需要将仰府和翻滚清0，所以只需要控制旋转的Yaw值。YawRotation(0.f, Rotation.Yaw, 0.f);
	const FRotator Rotation = GetControlRotation();
	const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
	//通过FRotationMatrix旋转矩阵的计算，从旋转变量中获取一个向前的变量，这里获取的是X轴是向前的方向
	//获取向右方向的向量就是获取Y轴方向
	//FRotationMatrix.GetUnitAxis中GetUnitAxis方法是获取一个长度为1的矢量，就是归一化
	const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
	//通过角色pawn的AddMovementInput方法设置对应方向的值，InputAxisVector参数中y轴是向前的轴,X轴是向右的值这里和之前的矩阵方法不同
	if (APawn* ControlledPawn = GetPawn<APawn>())
	{
		ControlledPawn->AddMovementInput(ForwardDirection, InputAxisVector.Y);
		ControlledPawn->AddMovementInput(RightDirection, InputAxisVector.X);
	}
}

void AStarrySkyPlayerController::AutoRun()
{
	if(!bAutoRunning) return;
	if(APawn* ControlledPawn = GetPawn<APawn>())
	{
		
		//获取控制的Pawn距离样条线最近的世界位置
		const FVector LocationOnSpline=Spline->FindLocationClosestToWorldLocation(ControlledPawn->GetActorLocation(),ESplineCoordinateSpace::World);
		//通过这个位置获取位置方向
		const FVector Direction=Spline->FindDirectionClosestToWorldLocation(LocationOnSpline,ESplineCoordinateSpace::World);
		//朝向位置移动
		ControlledPawn->AddMovementInput(Direction);
		//通过样条线获取到当前位置到目的地的距离
		const float DistanceToDestination=(LocationOnSpline-CachedDestination).Length();
		//如果距离小于阈值，则自动停止移动
		if(DistanceToDestination<=AutoRunAcceptanceRadius)
		{
			bAutoRunning=false;
		}
	}
}


void AStarrySkyPlayerController::LookUp(const float DeltaTime)
{
	// 检查是否有有效的光标命中结果
	if (CursorHit.bBlockingHit)
	{
		/**
		// 获取角色pawn
		if (APawn* ControlledPawn = GetPawn<APawn>())
		{
			// 获取角色当前位置
			FVector PawnLocation = ControlledPawn->GetActorLocation();
			
			// 计算从角色到命中点的方向向量
			FVector LookDirection = CursorHit.ImpactPoint - PawnLocation;
			LookDirection.Z = 0.f; // 保持在水平面上
			
			// 如果方向向量有效
			if (LookDirection.SizeSquared() > KINDA_SMALL_NUMBER)
			{
				// 归一化方向向量
				LookDirection.Normalize();
				
				// 计算目标旋转
				FRotator TargetRotation = LookDirection.Rotation();
				
				// 设置角色控制器的旋转
				//SetControlRotation(TargetRotation);
				// 或者如果需要平滑旋转，可以使用以下方式：
				ControlledPawn->SetActorRotation(TargetRotation);
			}
		}**/
		if (APawn* ControlledPawn = GetPawn())
		{
			FVector PawnLocation = ControlledPawn->GetActorLocation();
			FVector LookDirection = CursorHit.ImpactPoint - PawnLocation;
			LookDirection.Z = 0.f;
            
			if (LookDirection.SizeSquared() > KINDA_SMALL_NUMBER)
			{
				LookDirection.Normalize();
				TargetRotation = LookDirection.Rotation();
				bHasValidTarget = true;
			}
		}
		// 应用平滑旋转
		if (bHasValidTarget && GetPawn())
		{
			FRotator CurrentRotation = GetPawn()->GetActorRotation();
			//FMath::RInterpTo: 这是一个旋转插值函数，用于在两个旋转之间进行平滑过渡
			/**
			*参数说明:
			*	CurrentRotation: 当前旋转角度
			*	TargetRotation: 目标旋转角度
			*	DeltaTime: 帧间隔时间，确保帧率无关的平滑运动
			*	RotationInterpSpeed: 插值速度，控制旋转快慢
			 **/
			FRotator NewRotation = FMath::RInterpTo(
				CurrentRotation, 
				TargetRotation, 
				DeltaTime, 
				RotationInterpSpeed
			);
        
			GetPawn()->SetActorRotation(FRotator(0, NewRotation.Yaw, 0));
		}
	}
}

void AStarrySkyPlayerController::CursorTrace()
{
	/**
	 *GetHitResultUnderCursor 是Unreal Engine中用于执行点击测试（Hit Test）的函数，通常在游戏或应用中用于检测鼠标光标下方的对象。
	 *ECC_Visibility - 这是一个碰撞通道（Collision Channel），指定要检测的物体类型。ECC_Visibility 通常用于检测可见物体
	 **/
	//这里需要根据魔法圈是否激活来判断使用的检测通道
	GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);
	if (!CursorHit.bBlockingHit) return;
	//获取上一个和当前命中的Actor
	LastActor=ThisActor;
	ThisActor = Cast<IEnemyInterface>(CursorHit.GetActor());
	if(LastActor!=ThisActor)
	{
		if(LastActor) LastActor->UnHighlightActor();
		if(ThisActor) ThisActor->HighlightActor();
	}
}

void AStarrySkyPlayerController::AbilityInputTagPressed(FGameplayTag InputTag)
{
	//鼠标左键点击的时候进行的操作
	if(InputTag.MatchesTagExact(FStarrySkyGameplayTags::Get().InputTag_LMB))
	{
		bTargeting=ThisActor ? true : false;
		bAutoRunning=false;
	}
	//激活能量的输入按下方法
	if(GetAsc())
	{
		GetAsc()->AbilityInputTagPressed(InputTag);
	}
}

void AStarrySkyPlayerController::AbilityInputTagReleased(FGameplayTag InputTag)
{
	//不是鼠标左键就直接释放能量
	if(!InputTag.MatchesTagExact(FStarrySkyGameplayTags::Get().InputTag_LMB))
	{
		if(GetAsc())
		{
			GetAsc()->AbilityInputTagReleased(InputTag);
		}
		return;
	}
	//这里修改为只要是鼠标左键释放的时候，能力系统就直接执行输入释放的方法
	GetAsc()->AbilityInputTagReleased(InputTag);

	//并且鼠标左键释放的时候，如果当前没有目标，并且没有按下shift键，就进行移动的操作
	/**
	if(!bTargeting&&FollowTime<=ShortPressThreshold)
	{
		//获取控制器的所属的pawn作为导航的开始位置
		const APawn* ControlledPawn = GetPawn<APawn>();
		//判断是否是短按
		if(FollowTime<=ShortPressThreshold&&ControlledPawn)
		{
			//通过导航网格获取到目的地的路径点
			if(UNavigationPath* NavPath=UNavigationSystemV1::FindPathToLocationSynchronously(this,ControlledPawn->GetActorLocation(),CachedDestination))
			{
				Spline->ClearSplinePoints();
				for(const FVector& PointLocation:NavPath->PathPoints)
				{
					//添加路径点，世界坐标系
					Spline->AddSplinePoint(PointLocation,ESplineCoordinateSpace::World);
				}
				//将目标缓存点设置为路径的最后一个点
				if (NavPath->PathPoints.Num()>0)
				{
					CachedDestination=NavPath->PathPoints.Last();
					bAutoRunning=true;
				}
			}
		}
		FollowTime=0.f;
	}*/
}

void AStarrySkyPlayerController::AbilityInputTagHeld(FGameplayTag InputTag)
{
	if(!InputTag.MatchesTagExact(FStarrySkyGameplayTags::Get().InputTag_LMB))
	{
		if(GetAsc())
		{
			GetAsc()->AbilityInputTagHeld(InputTag);
		}
		return;
	}
	if(GetAsc())
	{
		GetAsc()->AbilityInputTagHeld(InputTag);
	}
	//获取鼠标光标命中的位置
	/**
	 * 这里不使用鼠标点中移动
	if(CursorHit.bBlockingHit)
	{
		CachedDestination=CursorHit.ImpactPoint;//获取碰撞点的位置缓存成为目的地
	}
	if (APawn* ControlledPawn = GetPawn<APawn>())
	{
		//世界方向，受控制的棋子到缓存目的地的向量,最后归一化
		const FVector WorldDirection = (CachedDestination-ControlledPawn->GetActorLocation()).GetSafeNormal();
		ControlledPawn->AddMovementInput(WorldDirection);
	}
	*/
}

UStarrySkyAbilitySystemComponent* AStarrySkyPlayerController::GetAsc()
{
	if(StarrySkyAbilitySystemComponent==nullptr)
	{
		//UAbilitySystemBlueprintLibrary通过这个静态函数库可以获取到能力系统组件，让后强转成UStarrySkyAbilitySystemComponent
		StarrySkyAbilitySystemComponent=Cast<UStarrySkyAbilitySystemComponent>(UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetPawn<APawn>()));
	}
	return StarrySkyAbilitySystemComponent;
}



